package com.expandable.view;

import com.expandable.HeightChangeListener;
import com.expandable.util.AnimatorValueUtil;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.*;
import ohos.agp.render.Canvas;
import ohos.app.Context;

import java.util.ArrayList;
import java.util.List;

/**
 * ExpandableView is a View showing only a visible content and when clicked on it, it displays more content in a fashion way.
 * It can also handle inner ExpandableViews, one ExpandableView inside another and another.
 */
public class ExpandableView extends DependentLayout implements Component.EstimateSizeListener, HeightChangeListener {

    private static final String TAG = "ExpandableView";
    private static final int DURATION = 400;

    private float DEGREES;

    private Text textView;

    private DependentLayout clickableLayout;

    private DirectionalLayout contentLayout;

    private List<ComponentContainer> outsideContentLayoutList;

    private int outsideContentHeight = 0;

    private Image leftIcon;

    private Image rightIcon;

    private AnimatorValue animator;
    private AnimatorValue rotateAnimator;
    private AnimatorValueUtil animatorValueUtil;
    private int height;
    private float degrees;
    int type;
    HeightChangeListener heightChangeListener;

    public void setHeightChangeListener(HeightChangeListener heightChangeListener) {
        this.heightChangeListener = heightChangeListener;
    }

    public ExpandableView(Context context) {
        super(context);
        init();
    }

    public ExpandableView(Context context, AttrSet attrs) {
        super(context, attrs);
        init();
    }

    public ExpandableView(Context context, AttrSet attrs, int defStyle) {
        super(context, attrs, "");
        init();
    }

    private void init() {
        type = 0;
        LayoutScatter.getInstance(this.mContext).parse(ResourceTable.Layout_expandable_view, this, true);
        outsideContentLayoutList = new ArrayList<>();
        textView = (Text) findComponentById(ResourceTable.Id_expandable_view_title);
        clickableLayout = (DependentLayout) findComponentById(ResourceTable.Id_expandable_view_clickable_content);
        contentLayout = (DirectionalLayout) findComponentById(ResourceTable.Id_expandable_view_content_layout);
        leftIcon = (Image) findComponentById(ResourceTable.Id_expandable_view_image);
        rightIcon = (Image) findComponentById(ResourceTable.Id_expandable_view_right_icon);
        clickableLayout.setClickedListener(new ClickedListener() {
            @Override
            public void onClick(Component component) {
                if (!contentLayout.isComponentDisplayed() && degrees == 0) {
                    expand();
                } else {
                    collapse();
                }
            }
        });
    }

    /**
     * Set the new Height of the visible content layout.
     *
     * @param newHeight
     */
    public void setVisibleLayoutHeight(int newHeight) {
        this.clickableLayout.getLayoutConfig().height = newHeight;
    }

    /**
     * Set the parent's ViewGroup
     *
     * @param outsideContentLayout
     */
    public void setOutsideContentLayout(ComponentContainer outsideContentLayout) {
        this.outsideContentLayoutList.add(outsideContentLayout);
    }

    /**
     * Set the parent's ViewGroup, one or more than one.
     *
     * @param params
     */
    public void setOutsideContentLayout(ComponentContainer... params) {
        for (int i = 0; i < params.length; i++) {
            this.outsideContentLayoutList.add(params[i]);
        }
    }

    /**
     * Return the TextView located in the visible content.
     *
     * @return Text对象
     */
    public Text getTextView() {
        return this.textView;
    }

    /**
     * Returns the Content LinearLayout, the LinearLayout that expands or collapse in a fashion way.
     *
     * @return The Content LinearLayout
     */
    public DirectionalLayout getContentLayout() {
        return this.contentLayout;
    }


    @Override
    public void heightChange(int childHeight) {

        contentLayout.addDrawTask(new DrawTask() {
            @Override
            public void onDraw(Component component, Canvas canvas) {
                if (type != 2) {
                    height = component.getHeight() - childHeight;
                    component.setVisibility(HIDE);
                    type = 2;
                }
            }
        });
        contentLayout.invalidate();
    }

    /**
     * Add a view into the Content LinearLayout, the LinearLayout that expands or collapse in a fashion way.
     *
     * @param newContentView
     */
    public void addContentView(Component newContentView) {
        ComponentContainer.LayoutConfig layoutConfig = new DirectionalLayout.LayoutConfig(
            ComponentContainer.LayoutConfig.MATCH_PARENT, DirectionalLayout.LayoutConfig.MATCH_CONTENT);
        newContentView.setLayoutConfig(layoutConfig);
        contentLayout.addComponent(newContentView);
        contentLayout.addDrawTask(new DrawTask() {
            @Override
            public void onDraw(Component component, Canvas canvas) {
                if (type == 0) {
                    type = 1;
                    height = component.getHeight();
                    if (heightChangeListener != null) {
                        heightChangeListener.heightChange(component.getHeight());
                    } else {
                        component.setVisibility(HIDE);
                    }
                }
            }
        });
        contentLayout.invalidate();
    }

    /**
     * Set the left drawable icon, the text to display, and if you want to use the chevron icon or plus icon.
     *
     * @param leftResId  - 0 for no left drawable.
     * @param text
     * @param useChevron - true to use the chevron icon, false to use the plus icon
     */
    public void fillData(int leftResId, String text, boolean useChevron) {
        textView.setText(text);
        if (leftResId == 0) {
            leftIcon.setVisibility(HIDE);
        } else {
            leftIcon.setPixelMap(leftResId);
        }
        if (useChevron) {
            DEGREES = 180;
            rightIcon.setPixelMap(ResourceTable.Media_ic_expandable_view_chevron);
        } else {
            DEGREES = -225;
            rightIcon.setPixelMap(ResourceTable.Media_ic_expandable_view_plus);
        }
    }

    /**
     * Expand animation to display the discoverable content.
     */
    public void expand() {
        contentLayout.setVisibility(Component.VISIBLE);
        rotateAnimator = new AnimatorValue();
        rotateAnimator.setCurveType(Animator.CurveType.LINEAR);
        rotateAnimator.setLoopedCount(0);
        rotateAnimator.setDuration(DURATION);
        this.rotateAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            public void onUpdate(AnimatorValue var1, float var2) {
                degrees = DEGREES * 1;
                float var3 = var2 * DEGREES;
                rightIcon.setRotation(var3);
            }
        });
        rotateAnimator.start();
        animator = slideAnimator(0, height);
        animator.start();
    }

    /**
     * Collapse animation to hide the discoverable content.
     */
    public void collapse() {
        int finalHeight = contentLayout.getHeight();
        rotateAnimator = new AnimatorValue();
        rotateAnimator.setCurveType(Animator.CurveType.LINEAR);
        rotateAnimator.setLoopedCount(0);
        rotateAnimator.setDuration(DURATION);
        AnimatorValue mAnimator = slideAnimator(finalHeight, 0);
        mAnimator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                contentLayout.setVisibility(Component.HIDE);
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }

        });
        this.rotateAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            public void onUpdate(AnimatorValue var1, float var2) {
                degrees = 0;
                float var3 = DEGREES * (1 - var2);
                rightIcon.setRotation(var3);
            }
        });
        rotateAnimator.start();
        mAnimator.start();
    }


    private AnimatorValue
    slideAnimator(int start, int end) {
        animator = new AnimatorValue();
        animator.setDuration(DURATION);
        animatorValueUtil = new AnimatorValueUtil();
        animatorValueUtil.setValues(new float[]{start, end});
        animatorValueUtil.ofFloat();
        animator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue valueAnimator, float value) {
                float malue = animatorValueUtil.getValue(value);
                ComponentContainer.LayoutConfig layoutParams = contentLayout.getLayoutConfig();
                layoutParams.height = (int) malue;
                contentLayout.setLayoutConfig(layoutParams);
                contentLayout.invalidate();
                if (outsideContentLayoutList != null && !outsideContentLayoutList.isEmpty()) {
                    for (ComponentContainer outsideParam : outsideContentLayoutList) {
                        ComponentContainer.LayoutConfig params = outsideParam.getLayoutConfig();
                        if (outsideContentHeight == 0) {
                            outsideContentHeight = params.height;
                        }
                        params.height = (int) (outsideContentHeight + malue);
                        outsideParam.setLayoutConfig(params);
                        outsideParam.invalidate();
                    }
                }
            }
        });
        return animator;
    }

    @Override
    public boolean onEstimateSize(int i, int i1) {
        return false;
    }
}
